package cn.itcast.nems.manager.kc;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.itcast.nems.manager.redis.RedisUtil;
import io.micrometer.common.util.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class OApiHttpUtil {
    /**
     * oapi地址
     */
    private String oapiUrl;
    /**
     * keycloak 应用id
     */
    private String keyCloakClientId;

    /**
     * keycloak manger
     */
    private KeyCloakManager keyCloakManager;
    /**
     * redis缓存工具类
     */
    private RedisUtil redisUtil;
    /**
     * 合同系统KeyCloak AccessToken缓存前缀
     */
    public static final String CONTRACT_KEYCLOAK_ACCESS_TOKEN = "EMSV2_KEYCLOAK_ACCESS_TOKEN:";

    public static final String OAPI_HEADER_AUTHORIZATION = "Authorization";
    public static final String OAPI_HEADER_NONCESTR = "noncestr";
    public static final String OAPI_HEADER_TIMESTAMP = "timestamp";
    public static final String OAPI_HEADER_SYSCLIENTID = "sysClientId";
    public static final String ECA_HEADER_PRINCIPALEMAIL = "Principal-Email";
    public static final String ECA_HEADER_DATASCOPE = "dataScope";
    public static final String BAREAR = "Bearer ";

    /**
     * 通过post请求eca，返回object格式数据
     * 
     * @param uri
     *            eca接口地址
     * @param body
     *            请求体Json字符串
     * @param userName
     *            用户邮箱
     * @param dataScopes
     *            数据权限范围
     * @param sysClientId
     *            数据权限范围
     * @return 接口响应
     */
    @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 100L))
    public JSONObject postForObject(String uri, String body, String userName, List<Integer> dataScopes,
                                    Integer sysClientId) {
        String keyCloakAccessToken;
        try {
            keyCloakAccessToken = keyCloakManager.getKeyCloakAccessToken();
        } catch (Exception e) {
            log.error("postForObject调用：{} 时获取KeyCloak应用token失败，失败原因：{}", uri, e);
            throw new RuntimeException("调用eca服务失败");
        }
        HttpResponse httpResponse = HttpRequest.get(oapiUrl + uri)
            .header(OAPI_HEADER_AUTHORIZATION, BAREAR + keyCloakAccessToken)
            .header(OAPI_HEADER_NONCESTR, UUID.randomUUID().toString())
            .header(OAPI_HEADER_TIMESTAMP, System.currentTimeMillis() + "")
            .header(OAPI_HEADER_SYSCLIENTID, sysClientId + "").header(ECA_HEADER_PRINCIPALEMAIL, userName)
            .header(ECA_HEADER_DATASCOPE, CollectionUtils.isEmpty(dataScopes) ? "" : dataScopes.stream().map(item-> "" + item).collect(Collectors.joining(",")))
            .body(body).execute();
        String responseBody = httpResponse.body();
        if (!httpResponse.isOk()) {
            log.error("getForObject请求：{} 失败，失败原因：{}", uri, httpResponse);
            if (responseBody.contains("Jwt expired at")) {
                redisUtil.del(CONTRACT_KEYCLOAK_ACCESS_TOKEN + keyCloakClientId);
                // 抛出异常进行重试
                throw new RuntimeException("请求eca token过期，进行重试");
            }
            JSONObject errorObject = JSONUtil.parseObj(responseBody);
            throw new RuntimeException(errorObject.getStr("message"));
        }
        return StringUtils.isBlank(responseBody) ? new JSONObject() : JSONUtil.parseObj(responseBody);
    }

    /**
     * 通过post请求eca，返回object格式数据
     * 
     * @param uri
     *            eca接口地址
     * @param userName
     *            用户邮箱
     * @param dataScopes
     *            数据权限范围
     * @param sysClientId
     *            ItcastCloud 客户端id
     * @return 接口响应
     */
    @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 100L))
    public JSONObject getForObject(String uri, String userName, List<Integer> dataScopes, Integer sysClientId) {
        String keyCloakAccessToken;
        try {
            keyCloakAccessToken = keyCloakManager.getKeyCloakAccessToken();
        } catch (Exception e) {
            log.error("getForObject调用：{} 时获取KeyCloak应用token失败，失败原因：{}", uri, e);
            throw new RuntimeException("调用eca服务失败");
        }
        HttpResponse httpResponse = HttpRequest.get(oapiUrl + uri)
            .header(OAPI_HEADER_AUTHORIZATION, BAREAR + keyCloakAccessToken)
            .header(OAPI_HEADER_NONCESTR, UUID.randomUUID().toString())
            .header(OAPI_HEADER_TIMESTAMP, System.currentTimeMillis() + "")
            .header(OAPI_HEADER_SYSCLIENTID, sysClientId + "").header(ECA_HEADER_PRINCIPALEMAIL, userName)
            .header(ECA_HEADER_DATASCOPE, CollectionUtils.isEmpty(dataScopes) ? "" : dataScopes.stream().map(item-> "" + item).collect(Collectors.joining(",")))
            .execute();
        String responseBody = httpResponse.body();
        if (!httpResponse.isOk()) {
            log.error("getForObject请求：{} 失败，失败原因：{}", oapiUrl+uri, httpResponse);
            if (responseBody.contains("Jwt expired at")) {
                redisUtil.del(CONTRACT_KEYCLOAK_ACCESS_TOKEN + keyCloakClientId);
                // 抛出异常进行重试
                throw new RuntimeException("请求eca token过期，进行重试");
            }
            JSONObject errorObject = JSONUtil.parseObj(responseBody);
            throw new RuntimeException(errorObject.getStr("message"));
        }
        return StringUtils.isBlank(responseBody) ? new JSONObject() : JSONUtil.parseObj(responseBody);
    }

    /**
     * 通过delete请求eca，返回object格式数据
     *
     * @param uri
     *            eca接口地址
     * @param userName
     *            用户邮箱
     * @param dataScopes
     *            数据权限范围
     * @param sysClientId
     *            ItcastCloud 客户端id
     * @return 接口响应
     */
    @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(delay = 100L))
    public JSONObject deleteForObject(String uri, String userName, List<Integer> dataScopes, Integer sysClientId) {
        String keyCloakAccessToken;
        try {
            keyCloakAccessToken = keyCloakManager.getKeyCloakAccessToken();
        } catch (Exception e) {
            log.error("deleteForObject调用：{} 时获取KeyCloak应用token失败，失败原因：{}", uri, e);
            throw new RuntimeException("调用eca服务失败");
        }
        HttpResponse httpResponse = HttpRequest.delete(oapiUrl + uri)
            .header(OAPI_HEADER_AUTHORIZATION, BAREAR + keyCloakAccessToken)
            .header(OAPI_HEADER_NONCESTR, UUID.randomUUID().toString())
            .header(OAPI_HEADER_TIMESTAMP, System.currentTimeMillis() + "")
            .header(OAPI_HEADER_SYSCLIENTID, sysClientId + "").header(ECA_HEADER_PRINCIPALEMAIL, userName)
            .header(ECA_HEADER_DATASCOPE, CollectionUtils.isEmpty(dataScopes) ? "" : dataScopes.stream().map(item-> "" + item).collect(Collectors.joining(",")))
            .execute();
        String responseBody = httpResponse.body();
        if (!httpResponse.isOk()) {
            log.error("deleteForObject请求：{} 失败，失败原因：{}", uri, httpResponse);
            if (responseBody.contains("Jwt expired at")) {
                redisUtil.del(CONTRACT_KEYCLOAK_ACCESS_TOKEN + keyCloakClientId);
                // 抛出异常进行重试
                throw new RuntimeException("请求eca token过期，进行重试");
            }
            JSONObject errorObject = JSONUtil.parseObj(responseBody);
            throw new RuntimeException(errorObject.getStr("message"));
        }
        return StringUtils.isBlank(responseBody) ? new JSONObject() : JSONUtil.parseObj(responseBody);
    }

}